package at.irian.jsfatwork.broker.domain.account;

import at.irian.jsfatwork.broker.domain.client.Client;
import at.irian.jsfatwork.broker.domain.client.ClientRepository;
import at.irian.jsfatwork.broker.domain.stock.Stock;
import org.springframework.stereotype.Repository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.dao.EmptyResultDataAccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.NoResultException;
import java.math.BigDecimal;

/**
 * @author Manfred Geiler
 */
@Repository
public class Accounting {

    private final Logger logger = LoggerFactory.getLogger(Accounting.class);

    @PersistenceContext
    private EntityManager em;

    @Transactional(propagation = Propagation.MANDATORY)
    public ClearingAccount createClearingAccount(Client client) {
        ClearingAccount account = new ClearingAccount();
        account.setClient(client);
        account.setAccountNumber(getNextAccountNr());
        account.setCurrency("EUR");
        em.persist(account);
        return account;
    }

    @Transactional(propagation = Propagation.MANDATORY)
    public SecuritiesAccount createSecuritiesAccount(Client client) {
        SecuritiesAccount account = new SecuritiesAccount();
        account.setClient(client);
        account.setAccountNumber(getNextAccountNr());
        em.persist(account);
        return account;
    }


    public ClearingAccount getClientClearingAccount(Client client) {
        return (ClearingAccount) em.createQuery("select a from ClearingAccount a where a.client = :client")
                .setParameter("client", client)
                .getSingleResult();
    }

    public SecuritiesAccount getClientSecuritiesAccount(Client client) {
        return (SecuritiesAccount) em.createQuery("select a from SecuritiesAccount a where a.client = :client")
                .setParameter("client", client)
                .getSingleResult();
    }


    @Transactional(propagation = Propagation.MANDATORY)
    public void buy(Client client, Stock stock, Integer quantity, BigDecimal amount) {
        getClientClearingAccount(client).debit(amount);
        getClientSecuritiesAccount(client).credit(stock, quantity);
    }

    @Transactional(propagation = Propagation.MANDATORY)
    public void sell(Client client, Stock stock, Integer quantity, BigDecimal amount) {
        getClientClearingAccount(client).credit(amount);
        getClientSecuritiesAccount(client).debit(stock, quantity);
    }

    public void validateBuy(Client client, BigDecimal amount) {
        getClientClearingAccount(client).validateCovered(amount);
    }

    public void validateSell(Client client, Stock stock, Integer quantity) {
        getClientSecuritiesAccount(client).validateCovered(stock, quantity);
    }

    private Integer accountNr = 0;

    public synchronized String getNextAccountNr() {
        return "100200" + (accountNr++ * 100);
    }


    @Transactional(propagation = Propagation.MANDATORY)
    public void deleteAllClientAccounts(Client client) {
        try {
            ClearingAccount clearingAccount = getClientClearingAccount(client);
            em.remove(clearingAccount);
        } catch (NoResultException e) {
            logger.warn("Client has no ClearingAccount - nothing to delete");
        }

        try {
            SecuritiesAccount securitiesAccount = getClientSecuritiesAccount(client);
            em.remove(securitiesAccount);
        } catch (NoResultException e) {
            logger.warn("Client has no SecuritiesAccount - nothing to delete");
        }
    }
}
